#include "TokenGenerator.h"
#include "ScriptCompiler/ScriptFileContext.h"
#include "Token.h"

#include <numeric>


CTokenGenerator::CTokenGenerator(std::shared_ptr<CScriptFileContext> InContext)
{
    Context=InContext;
}

void CTokenGenerator::GenerateTokens()
{
    while(CharIndex<Context->FileContent.Num())
    {
        if(!TryCreateToken())
        {
            return;
        }
    }
}

bool CTokenGenerator::TryCreateToken()
{
    auto CurrentChar=GetCurrentChar();
    switch(CurrentChar)
    {
        case U'\0':
        {
           
            //add end of file token
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::EndOfFile,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;     
        }
        case U' ':case U'\t':case U'\r':case U'\n':
        {
           
            //skip space
            GotoNextChar();
            return true;
        }
        case U'*':
        {
           if(GetNextChar()==U'=')
           {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::MultiplyAssign,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
           }
           else
           {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Multiply,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
           }
            return true;
        }
        case U'/':
        {
            if(GetNextChar()==U'/')
            {
               

                //skip comment
                while(GetCurrentChar()!=U'\n' &&  GetCurrentChar()!=U'\0'  )
                {
                    GotoNextChar();
                }
                return true;
            }
            else if(GetNextChar()==U'*')
            {
               

                //skip comment
                GotoNextChar();
                while(GetCurrentChar()!=U'*' || GetNextChar()!=U'/')
                {
                    GotoNextChar();
                }
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::DivideAssign,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }

            else 
            {
               

                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Divide,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'"':
        {
                                                   
            //literal string
            CString LiteralString;
            GotoNextChar();
            while(GetCurrentChar()!=U'"')
            {
                if(GetCurrentChar()==U'\0')
                {
                    Context->RaiseError(10140,LineIndex,ColumnIndex,1,U"Literal string not end");
                    return false;
                }

                char32_t LiteralChar=GetCurrentChar();
                if(LiteralChar==U'\\')
                {
                    auto EscapeChar=ReadEscapeChar();
                    if(!EscapeChar.HasValue())
                    {
                        return false;
                    }
                    LiteralChar=EscapeChar.GetValue();
                }

                LiteralString+=LiteralChar;
                GotoNextChar();
            }
            GotoNextChar();
            Context->TokenList.Add(
                    std::make_shared<CScriptToken>(
                        LiteralString
                        ,SScriptFilePosition(LineIndex,ColumnIndex,LiteralString.GetLength()+2)
                        )
            );
            return true;                                            

        }

        case U'\'':
        {                    
            //literal char
            GotoNextChar();
            if(GetCurrentChar()==U'\'')
            {
                Context->RaiseError(10141,LineIndex,ColumnIndex,1,U"Literal char not end");
                return false;
            }
            char32_t LiteralChar=GetCurrentChar();
            if(LiteralChar==U'\\')
            {
                auto EscapeChar=ReadEscapeChar();
                if(!EscapeChar.HasValue())
                {
                    return false;
                }
                LiteralChar=EscapeChar.GetValue();
            }

            GotoNextChar();
            if(GetCurrentChar()!=U'\'')
            {
                Context->RaiseError(10142,LineIndex,ColumnIndex,1,U"Literal char not end");
                return false;
            }
            GotoNextChar();
            Context->TokenList.Add(std::make_shared<CScriptToken>(LiteralChar,SScriptFilePosition(LineIndex,ColumnIndex,3)));
            return true;                                        
        }

        case U'?':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Question,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }


        case U'(':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::LeftParentheses,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U')':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::RightParentheses,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'{':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::LeftBrace,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'}':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::RightBrace,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'[':
        {   
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::LeftBracket,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U']':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::RightBracket,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U',':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Comma,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U';':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Semicolon,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U':':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Colon,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'.':
        {
           
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Dot,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'!':
        {
           
            if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::NotEqual,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::NotOperator,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'=':
        {
           
            if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Equal,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            if(GetNextChar()==U'>')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::LambdaOperator,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Assign,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'<':
        {
           
            if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::LessEqual,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Less,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'>':
        {
           
            if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::GreaterEqual,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Greater,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'|':
        {
            if(GetNextChar()==U'|')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::OrOr,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Or,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'&':
        {
            if(GetNextChar()==U'&')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::AndAnd,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::And,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'+':
        {
           
            if(GetNextChar()==U'+')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::PlusPlus,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::PlusAssign,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }

            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Plus,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }

        case U'-':
        {
           
            if(GetNextChar()==U'-')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::MinusMinus,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else if(GetNextChar()==U'=')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::MinusAssign,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }
            else if(GetNextChar()==U'>')
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Arrow,SScriptFilePosition(LineIndex,ColumnIndex,2)));
                GotoNextChar();
                GotoNextChar();
                return true;
            }

            else
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Minus,SScriptFilePosition(LineIndex,ColumnIndex,1)));
                GotoNextChar();
                return true;
            }
        }
        case U'~': 
        {
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Tilde,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }     
        
        case U'@':
        {
            Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::At,SScriptFilePosition(LineIndex,ColumnIndex,1)));
            GotoNextChar();
            return true;
        }

        case U'0':
        case U'1':
        case U'2':
        case U'3':
        case U'4':
        case U'5':
        case U'6':
        case U'7':
        case U'8':
        case U'9':
        {
            CString NumberString;
            CString NumberStringAfterDot;
            bool IsFloat=false;

            do{
                if(GetCurrentChar()==U'.')
                {
                    if(IsFloat)
                    {
                        Context->RaiseError(10143,LineIndex,ColumnIndex,1,U"Invalid number");
                        return false;
                    }
                    else
                    {
                        if(GetNextChar()>=U'0' && GetNextChar()<=U'9' )
                        {
                            //  next char of '.' is number
                            IsFloat=true;
                        }
                        else
                        {
                            // trying access number's member
                            break;
                        }
                    }

                    GotoNextChar();
                    continue;
                }
                
                if(IsFloat)
                {
                    NumberStringAfterDot+=GetCurrentChar();
                }
                else
                {
                    NumberString+=GetCurrentChar();
                }
                GotoNextChar();
            }
            while((GetCurrentChar()>=U'0' && GetCurrentChar()<=U'9')
                || GetCurrentChar()==U'.'
            );

            if(IsFloat)
            {
                double Number= (NumberString+U'.'+NumberStringAfterDot).ToDouble(); //   std::stod(std::string(NumberString.begin(),NumberString.end())+"."+std::string(NumberStringAfterDot.begin(),NumberStringAfterDot.end()));

                // if(Number < std::numeric_limits<float>::lowest() || Number > std::numeric_limits<float>::max())
                // {
                //     Context->TokenList.Add(std::make_shared<CScriptToken>((float)Number,SScriptFilePosition(LineIndex,ColumnIndex,NumberString.GetLength()+NumberStringAfterDot.GetLength()+1)));
                //     return false;
                // }
                //else
                {
                    Context->TokenList.Add(std::make_shared<CScriptToken>(Number,SScriptFilePosition(LineIndex,ColumnIndex,NumberString.GetLength()+NumberStringAfterDot.GetLength()+1)));
                }

            }
            else
            {
                uint64_t Number=NumberString.ToUInt64();
                Context->TokenList.Add(std::make_shared<CScriptToken>(Number,SScriptFilePosition(LineIndex,ColumnIndex,NumberString.GetLength())));
            }
            return true;
        }



        // case U'a': case U'b': case U'c': case U'd': case U'e': case U'f': case U'g': case U'h': case U'i': case U'j': case U'k': case U'l': case U'm': case U'n': case U'o': case U'p': case U'q': case U'r': case U's': case U't': case U'u': case U'v': case U'w': case U'x': case U'y': case U'z':
        // case U'A': case U'B': case U'C': case U'D': case U'E': case U'F': case U'G': case U'H': case U'I': case U'J': case U'K': case U'L': case U'M': case U'N': case U'O': case U'P': case U'Q': case U'R': case U'S': case U'T': case U'U': case U'V': case U'W': case U'X': case U'Y': case U'Z':
        // case U'_':
        default: 
        {
            if(
                !((GetCurrentChar()>=U'a' && GetCurrentChar()<=U'z')
                || (GetCurrentChar()>=U'A' && GetCurrentChar()<=U'Z')
                || GetCurrentChar()==U'_'
                || GetCurrentChar()>=0x80)
            )
            {
                //invalid identifier
                Context->RaiseError(10144,LineIndex,ColumnIndex,1,U"Invalid identifier");
                return false;
            }
            //parse identifier
            {
                CString IdentifierString;
                do{
                    IdentifierString+=GetCurrentChar();
                    GotoNextChar();
                }
                while((GetCurrentChar()>=U'a' && GetCurrentChar()<=U'z') 
                || (GetCurrentChar()>=U'A' && GetCurrentChar()<=U'Z') 
                || (GetCurrentChar()>=U'0' && GetCurrentChar()<=U'9') 
                || GetCurrentChar()==U'_'
                || GetCurrentChar()>=0x80
                );
                
                if(!TryCreateKeywordToken(IdentifierString))
                {
                    auto IdentifierToken=std::make_shared<CScriptToken>(EScriptTokenType::Identifier,SScriptFilePosition(LineIndex,ColumnIndex,IdentifierString.GetLength()));
                    IdentifierToken->LiteralValue=IdentifierString;
                    Context->TokenList.Add(IdentifierToken);
                }
                return true;


            }
        }

        // default:    
        //     assert(false && "unreachable code");
        //     return false;

    }

}

bool CTokenGenerator::TryCreateKeywordToken(CString InWord)
{
    switch(InWord[0])
    {
        case U'a':
        {
            if(InWord==U"abstract")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Abstract,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"as")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::As,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"and")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::And,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"add")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Add,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"async")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Async,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"await")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Await,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"alias")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Alias,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else
            {
                return false;
            }

            break;

        }
        case U'b':
        {
            if(InWord==U"break")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Break,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"bool")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Bool,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"byte")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Uint8,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"base")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Base,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"by")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::By,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }

            else
            {
                return false;
            }
            break;
        }

        case U'c':
        {
            if(InWord==U"class")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Class,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"const")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Const,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"continue")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Continue,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"case")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Case,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                
            }
            else if(InWord==U"char")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Char,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"catch")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Catch,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"checked")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Checked,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
            }
            else
            {
                return false;
            }
            break;
        }

        case U'd':
        {
            if(InWord==U"do")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Do,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"default")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Default,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"delegate")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Delegate,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                
            }
            else if(InWord==U"double")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Double,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                
            }
            else if(InWord==U"decimal")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Decimal,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                
            }
            else if(InWord==U"dynamic")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Dynamic,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                  
            }

            else
            {
                return false;
            }

            break;
        }

        case U'e':
        {
            if(InWord==U"else")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Else,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"enum")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Enum,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"extern")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Extern,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"event")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Event,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"explicit")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Explicit,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else
            {
                return false;
            }
            break;
        }

        case U'f':
        {
            if(InWord==U"for")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::For,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"false")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(false,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"finally")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Finally,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"fixed")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Fixed,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"float")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Float,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"from")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::From,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else if(InWord==U"foreach")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Foreach,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            else
            {
                return false;
            }
            break;
        }

        case U'g':
        {
            if(InWord==U"get")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Get,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                    
            }
            else if(InWord==U"goto")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Goto,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"global")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Global,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }    
            else if(InWord==U"group")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Group,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            } 
            else
            {
                return false;
            }

            break;
        }

        case U'h':
            return false;

        case U'i':
        {
            if(InWord==U"if")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::If,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"int32")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Int32,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"int")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Int64,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"interface")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Interface,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"internal")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Internal,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"in")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::In,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"implicit")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Implicit,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"into")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Into,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"is")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Is,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;                   
            }
            else if(InWord==U"init")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Init,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else
            {
                return false;
            }

        }

        case U'j':
        {
            if(InWord==U"join")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Join,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else
            {
                return false;
            }

        }

        case U'k':
        {
                return false;
        }

        case U'l':
        {
            if(InWord==U"let")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Let,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"lock")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Lock,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            // else if(InWord==U"long")
            // {
            //     Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Long,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
            //     return true;
            // }
            else 
            {
                return false;
            }

        }
        
        case U'm':
        {
            return false;
        }

        case U'n':
        {
            if(InWord==U"new")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::New,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"null")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Null,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"namespace")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Namespace,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"not")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Not,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if((InWord==U"notnull"))
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::NotNull,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;               
            }
            // else if((InWord==U"nuint"))
            // {
            //     Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Nuint,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
            //     return true;               
            // }
            // else if((InWord==U"nint"))
            // {
            //     Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Nint,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
            //     return true;               
            // }            

            else 
            {
                return false;
            }
        }

        case U'o':
        {
            if(InWord==U"object")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Object,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"out")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Out,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"override")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Override,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"operator")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Operator,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"or")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Or,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"on")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::On,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }

        }

        case U'p':
        {
            if(InWord==U"private")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Private,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"protected")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Protected,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"public")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Public,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"partial")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Partial,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"params")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Params,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }

        }

        case U'q':
            return false;

        case U'r':
        {
            if(InWord==U"ref")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Ref,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"return")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Return,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"record")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Record,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"remove")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Remove,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }

        }

        case U's':
        {
            if(InWord==U"string")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::String,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"struct")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Struct,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"static")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Static,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"switch")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Switch,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"short")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Int16,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"sizeof")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Sizeof,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"set")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Set,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"sbyte")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Int8,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"sealed")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Sealed,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }            

        }

        case U't':
        {
            if(InWord==U"true")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(true,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"this")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::This,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"throw")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Throw,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"try")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Try,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"typeof")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Typeof,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }          
        }

        case U'u':
        {
            if(InWord==U"uint32")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Uint32,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"uint")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Uint64,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"ushort")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Uint16,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"using")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Using,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"unsafe")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Unsafe,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }            

        }

        case U'v':
        {
            if(InWord==U"void")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptInnerType::Void,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"virtual")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Virtual,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"volatile")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Volatile,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"var")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Var,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"value")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Value,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }            
            else 
            {
                return false;
            }            
        }

        case U'w':
        {
            if(InWord==U"while")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::While,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"where")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Where,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"when")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::When,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else if(InWord==U"with")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::With,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }   


        }

        case U'x':
            return false;


        case U'y':
        {
            if(InWord==U"yield")
            {
                Context->TokenList.Add(std::make_shared<CScriptToken>(EScriptTokenType::Yield,SScriptFilePosition(LineIndex,ColumnIndex- InWord.GetLength() +1,InWord.GetLength())));
                return true;
            }
            else 
            {
                return false;
            }   

        }

        case U'z':
            return false;

        

    }

    return false;
}

char32_t CTokenGenerator::GetCurrentChar() const
{
    return Context->FileContent[CharIndex];
}

char32_t CTokenGenerator::GetNextChar() const
{
    return Context->FileContent[CharIndex+1];
}

void CTokenGenerator::GotoNextChar()
{
    auto CurrentChar=GetCurrentChar();
   
    

    CharIndex++;

    if(CurrentChar==U'\n')
    {
        LineIndex++;
        ColumnIndex=0;

        Context->LineStartIndexList.Add(CharIndex);
    }
    else
    {
        ColumnIndex++;
    }

}

TOptional<char32_t> CTokenGenerator::ReadEscapeChar()
{
    auto CurrentChar=GetCurrentChar();
    assert(CurrentChar==U'\\');

    GotoNextChar();

    auto NextChar=GetCurrentChar();

    switch (NextChar)
    {
        case U'\'':
            return U'\'';
        
        case U'\"':
            return U'\"';
        
        case U'?':
            return U'\?';
        
        case U'\\':
            return U'\\';
        
        case U'a':
            return U'\a';
        
        case U'b':
            return U'\b';
        
        case U'f':
            return U'\f';
        
        case U'n':
            return U'\n';
        
        case U'r':
            return U'\r';
        
        case U't':
            return U'\t';
        
        case U'v':
            return U'\v';
        
        case U'0':
            return U'\0';
    }

    Context->RaiseError(10144,SScriptFilePosition(LineIndex,ColumnIndex,1),U"Invalid escape char");
    return NullOpt;
}
